#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include<functional>
#include"log.hpp"
#include"protocol.hpp"
#define NUM 1024

using namespace std;
static const int gbacklog = 5;
namespace Server
{
    enum
    {
        USAGE_ERR=1,SOCK_ERR,BIND_ERR,LISTEN_ERR
    };

typedef function<bool(const Request&req,Response& res)> func_t;

void handlerentry(int sock,func_t func)
{
     string inbuffer;
    while(true)
    {
    //1. 获取客户端发送来的数据报,确定数据报是带报头的数据报
    //"text_len"\r\n"x op y"\r\n 
   string req_text,req_str;
    if(!recvPackage(sock,inbuffer,&req_text)) return;
    cout<<"带报头的请求（数据报）: "<<req_text<<endl;
    //2.对数据报进行反序列化
    //"text_len"\r\n"x op y"\r\n -> "x op y"---去掉报头，取出里面的内容
    if(!deLength(req_text,&req_str)) return;
    cout<<"去掉报头的请求（数据报）:"<<req_str<<endl;
    //走到这里再往下就卡主了，只打印到上面那条日志后面都没打印到！！！
    //3.获得一个结构化的请求对象
    Request req;
   if(!req.Deserialize(req_str)) return;//如果反序列化失败直接返回


   
    //4.对对象进行操作---进行服务器业务
    //4.1.获得一个结构化响应
     Response rep;
    func(req,rep);
    //5.对对象进行序列化
    //_exitcode _result ->"_exitcodeSEP_result"
    string rep_str;
    rep.Serialize(&rep_str);
    cout<<"计算完成后的响应： "<<rep_str<<endl;
    //6.給有效载荷加上报头
     //"exitcode result" -> "content_len"\r\n"exitcode result"\r\n
    string rep_text=enLength(rep_str);
    cout<<"加上报头的完整响应（报文）: "<<rep_text<<endl;

    //7.把报文发送回給客户端
    send(sock,rep_text.c_str(),rep_text.size(),0);
    
    }
    


}
typedef function<bool(const Request&req,Response& res)> func_t;
class calserver
{
 
public:
calserver(const uint16_t& port):_port(port),_listensock(-1){}


void initserver()
{
//1.创建套接字
_listensock=socket(AF_INET,SOCK_STREAM,0);
if(_listensock<0)
{
    logMessage(FATAL,"create listensocket error");
    exit(SOCK_ERR);
}
 logMessage(NORMAL, "create socket success: %d", _listensock);
//2.bind ip和port
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(_port);
local.sin_addr.s_addr=INADDR_ANY;
if(bind(_listensock,(struct sockaddr*)&local,sizeof(local))<0)//绑定失败
{
    logMessage(FATAL,"bind error");
    exit(BIND_ERR);
}
 logMessage(NORMAL,"bind success");
//3.将套接字设置为监听模式
if(listen(_listensock,gbacklog)<0)
{
    logMessage(FATAL,"listen error");
    exit(LISTEN_ERR);
}
logMessage(NORMAL,"listen success");
}

void start(func_t fun)
{
    while(true)
    {
        struct sockaddr_in cli;
        socklen_t len=sizeof(cli);
        bzero(&cli,len);

        int sock=accept(_listensock,(struct sockaddr*)&cli,&len);
        if(sock<0)
        {
            logMessage(FATAL,"accept client error");
            continue;
        }
        logMessage(NORMAL,"accept client success");

        cout<<"accept sock: "<<sock<<endl;

        //多进程版---
        //一个客户端占用一个文件描述符，原因在于孙子进程执行IO任务需要占用独立的文件描述符,而文件描述符是继承父进程的，而每次客户端进来都要占用新的文件描述符
        //因此若接收多个客户端不退出的话文件描述符会越来越少。
         pid_t id=fork();//创建子进程
         if(id==0)//子进程进入
         {
             close(_listensock);//子进程不需要用于监听因此关闭该文件描述符
             handlerentry(sock,fun);
             close(sock);
             exit(0);
        }
        //父进程
        close(sock);
        pid_t ret=waitpid(id,nullptr,0);
        if(ret<0)
        {
            cout << "waitsuccess: " << ret << endl;
        }
    }
}


~calserver(){}

private:
int _listensock;//用于监听服务器的sock文件描述符
uint16_t _port;//端口号
};

}